﻿/*
 * 04/09/2010 - 1.0.0.1 - Eber Irigoyen
 * - Added support for context connection, execution wrapper should not open or close this connection
 */
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;

namespace AbstractSql {
    class AbsDBCommandExecution : IAbsDBCommandExecution {
        bool isLive;
        bool useDefaultConnection;
        string connectionName;
        CommandType commandType;
        string commandText;
        SqlConnection sqlConnection;
        List<SqlParameter> parameters = new List<SqlParameter>();

        internal CommandType CommandType { set { commandType = value; } }
        internal string CommandText { set { commandText = value; } }
        internal SqlConnection SqlConnection { set { sqlConnection = value; } }

        #region Private helper methods
        SqlConnection GetSqlConnection() {
            return sqlConnection ??
                (useDefaultConnection ? AbsDBConnection.GetDefaultConnection() : AbsDBConnection.GetNamedConnection(connectionName));
        }
        void ExecutionWrapper(Action<SqlCommand> commandAction) {
            SqlConnection conn = GetSqlConnection();
            try {
                using (SqlCommand comm = new SqlCommand(commandText, conn)) {
                    comm.CommandType = commandType;
                    comm.CommandTimeout = AbsDBConnection.DefaultTimeOut;
                    foreach (SqlParameter p in parameters)
                        comm.Parameters.Add(p);
                    if (!isLive)
                        conn.Open();
                    commandAction(comm);
                }
            }
            finally {
                if (!isLive && conn != null)
                    conn.Dispose();
            }
        }
        #endregion
        internal AbsDBCommandExecution() {
            useDefaultConnection = true;
        }
        internal AbsDBCommandExecution(string connName) {
            useDefaultConnection = string.IsNullOrEmpty(connName);
            connectionName = connName;
        }
        internal AbsDBCommandExecution(SqlConnection conn) {
            SqlConnection = conn;
            isLive = true;
        }
        public IAbsDBCommandExecution WithParam(string paramName, object paramValue) {
            parameters.Add(new SqlParameter(paramName, paramValue));
            return this;
        }
        public IAbsDBCommandExecution WithParam<T>(string paramName, T paramValue) {
            parameters.Add(new SqlParameter(paramName, paramValue));
            return this;
        }
        public IAbsDBCommandExecution WithParam<T>(string paramName, SqlDbType dbType, T paramValue) {
            SqlParameter p = new SqlParameter(paramName, dbType);
            p.Value = paramValue;
            parameters.Add(p);
            return this;
        }
        public void ExecuteNonQuery() {
            ExecutionWrapper(delegate(SqlCommand comm) { comm.ExecuteNonQuery(); });
        }
        public T ExecuteScalar<T>() {
            T result = default(T);
            ExecutionWrapper(delegate(SqlCommand comm) {
                object r = comm.ExecuteScalar();
                if (r == DBNull.Value)
                    r = null;
                result = (T)r;
            });
            return result;
        }
        public T ExecuteScalar<T>(T defaultValue) {
            T result = default(T);
            ExecutionWrapper(delegate(SqlCommand comm) {
                object r = comm.ExecuteScalar();
                if (r == null || r == DBNull.Value)
                    r = defaultValue;
                result = (T)r; });
            return result;
        }
        public void ExecuteReaderSingle(Action<IDataReader> action) {
            ExecutionWrapper(delegate(SqlCommand comm) {
                using (IDataReader reader = comm.ExecuteReader()) {
                    if (reader.Read())
                        action(reader);
                }
            });
        }
        public T ExecuteReaderSingle<T>(ModelMapper<T> mapper) where T : class, new() {
            T model = null;
            ExecutionWrapper(delegate(SqlCommand comm) {
                using (IDataReader r = comm.ExecuteReader()) {
                    if (r.Read()) {
                        model = mapper.Map(r);
                    }
                }
            });
            return model;
        }
        public void ExecuteReader(Action<IDataReader> action) {
            ExecutionWrapper(delegate(SqlCommand comm) {
                using (IDataReader reader = comm.ExecuteReader()) {
                    while (reader.Read())
                        action(reader);
                }
            });
        }
        public DataTable AsDataTable() {
            DataTable result = new DataTable();
            ExecutionWrapper(
                delegate(SqlCommand comm) {
                    SqlDataAdapter adp = new SqlDataAdapter(comm);
                    adp.Fill(result);
                });
            return result;
        }
        public List<T> AsList<T>() {
            List<T> result = new List<T>();
            ExecutionWrapper(delegate(SqlCommand comm) {
                using (IDataReader reader = comm.ExecuteReader()) {
                    while (reader.Read()) {
                        object value = reader[0];
                        if (value == DBNull.Value)
                            value = null;
                        result.Add((T)value);
                    }
                }
            });
            return result;
        }
        public List<T> AsList<T>(ModelMapper<T> mapper) {
            List<T> result = new List<T>();
            ExecutionWrapper(delegate(SqlCommand comm) {
                using (IDataReader reader = comm.ExecuteReader()) {
                    while (reader.Read()) {
                        result.Add(mapper.Map(reader));
                    }
                }
            });
            return result;
        }
        public Hashtable AsHashTable() {
            Hashtable result = null;
            ExecutionWrapper(delegate(SqlCommand comm) {
                using (IDataReader reader = comm.ExecuteReader()) {
                    if (reader.Read()) {
                        result = new Hashtable(reader.FieldCount);
                        for (int col = 0; col < reader.FieldCount; col++) {
                            string colName = reader.GetName(col);
                            object value = reader.GetValue(col);
                            if (value == DBNull.Value)
                                value = null;
                            result.Add(colName, value);
                        }
                    }
                }
            });
            return result == null ? new Hashtable() : result;
        }
    }
}